package com.demo.config;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;

/**
 * @author wangfengchen
 */
@Configuration
public class RestTemplateConfig {

  /**
   * 最大连接数
   */
  @Value("${rest-template-config.maxTotal:500}")
  private int maxTotal;
  /**
   * 每个路由的默认最大连接数
   */
  @Value("${rest-template-config.defaultMaxPerRoute:300}")
  private int defaultMaxPerRoute;
  /**
   * 返回数据的超时时间
   */
  @Value("${rest-template-config.socketTimeout:20000}")
  private int socketTimeout;
  /**
   * 建立连接的超时时间
   */
  @Value("${rest-template-config.connectTimeout:10000}")
  private int connectTimeout;
  /**
   * 从连接池中获取连接的超时时间
   */
  @Value("${rest-template-config.connectionRequestTimeout:1000}")
  private int connectionRequestTimeout;

  @Bean
  public RestTemplate restTemplate(ClientHttpRequestFactory requestFactory) {
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    // 设置字符串转换器编码
    restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
    // 自定义参数转换，解决部分微信api MediaType不匹配问题
    // restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
    // 错误拦截
    restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
    return restTemplate;
  }

  @Bean
  public ClientHttpRequestFactory clientHttpRequestFactory() {
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    factory.setReadTimeout(10000);
    factory.setConnectTimeout(10000);
    factory.setConnectionRequestTimeout(10000);
    factory.setHttpClient(httpClient());
    return factory;
  }

  @Bean
  public HttpClient httpClient() {
    Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
    connectionManager.setMaxTotal(maxTotal);
    // 路由指的是按主机对 MaxTotal 进行再分配。比如：MaxTotal=1000，DefaultMaxPerRoute=500，
    // 而服务只连接到 https://a.com 时，到这个主机的并发最多只有 500；而不是 1000；
    // 而我连接到 https://a.com 和 https://b.com 时，到每个主机的并发最多只有 500；即加起来是 1000（但不能超过 1000)
    // 而 DefaultMaxPerRoute 与 MaxPerRoute 的区别是：前者是所有路由的默认值，后者是可以单独为特定的路由进行配置。
    connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
    RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
            .setConnectionRequestTimeout(connectionRequestTimeout).build();
    return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
  }

}
